﻿//"The contents of this file are subject to the Mozilla Public License
//Version 1.1 (the "License"); you may not use this file except in
//compliance with the License. You may obtain a copy of the License at
//http://www.mozilla.org/MPL/

//Software distributed under the License is distributed on an "AS IS"
//basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//License for the specific language governing rights and limitations
//under the License.

//The Original Code is TSharp.

//The Initial Developer of the Original Code is Mar3ek (http://mar3ek.wordpress.com).
//Portions created by Mar3ek are Copyright (C) 2010
//Mar3ek (http://mar3ek.wordpress.com). All Rights Reserved.

//Contributor(s): ______________________________________.

//Alternatively, the contents of this file may be used under the terms
//of the _____ license (the  "[___] License"), in which case the
//provisions of [______] License are applicable instead of those
//above.  If you wish to allow use of your version of this file only
//under the terms of the [____] License and not to allow others to use
//your version of this file under the MPL, indicate your decision by
//deleting  the provisions above and replace  them with the notice and
//other provisions required by the [___] License.  If you do not delete
//the provisions above, a recipient may use your version of this file
//under either the MPL or the [___] License."

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Xml.Linq;

namespace TSharp
{
    public partial class Twitter
    {
        #region Lists

        #region CreateList

        /// <summary>
        /// Creates a new list for the authenticated user
        /// </summary>
        /// <param name="listName">The name for the list</param>
        /// <param name="screenName">Twitter username - must be valid, doensn't have to belong to the authenticated user</param>
        /// <param name="mode">Whether your list is public or private</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>Created list</returns>
        /// <remarks>Accounts are limited to 20 lists.</remarks>
        public TwitterList CreateList(string listName, string screenName, ListMode mode)
        {
            if (string.IsNullOrEmpty(listName)) throw new ArgumentNullException("listName");
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");

            string listModeParam;

            switch (mode)
            {
                case ListMode.Private:
                    listModeParam = "private";
                    break;
                default:
                    listModeParam = "public";
                    break;
            }

            var result = SendRequest(RequestMethod.Post,
                                        string.Format("https://api.twitter.com/1/{0}/lists.xml", screenName),
                                        false,
                                        new KeyValuePair<string, string>("name", listName),
                                        new KeyValuePair<string, string>("mode", listModeParam));

            var doc = XDocument.Parse(result);
            return ParseList(doc.Element("list"));
        }

        /// <summary>
        /// Creates a new list for the authenticated user
        /// </summary>
        /// <param name="listName">The name for the list</param>
        /// <param name="user">Twitter user - must be valid, doensn't have to be the authenticated user</param>
        /// <param name="mode">Whether your list is public or private</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>Created list</returns>
        /// <remarks>Accounts are limited to 20 lists.</remarks>
        public TwitterList CreateList(string listName, TwitterUser user, ListMode mode)
        {
            if (string.IsNullOrEmpty(listName)) throw new ArgumentNullException("listName");
            if (user == null) throw new ArgumentNullException("user");

            return CreateList(listName, user.ScreenName, mode);
        }

        /// <summary>
        /// Creates a new list for the authenticated user
        /// </summary>
        /// <param name="listName">The name for the list</param>
        /// <param name="screenName">Twitter username - must be valid, doensn't have to belong to the authenticated user</param>
        /// <param name="description">The description to give the list</param>
        /// <param name="mode">Whether your list is public or private</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>Created list</returns>
        /// <remarks>Accounts are limited to 20 lists.</remarks>
        public TwitterList CreateList(string listName, string screenName, string description, ListMode mode)
        {
            if (string.IsNullOrEmpty(listName)) throw new ArgumentNullException("listName");
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (string.IsNullOrEmpty(description)) return CreateList(listName, screenName, mode);

            string listModeParam;

            switch (mode)
            {
                case ListMode.Private:
                    listModeParam = "private";
                    break;
                default:
                    listModeParam = "public";
                    break;
            }

            var result = SendRequest(RequestMethod.Post,
                                        string.Format("https://api.twitter.com/1/{0}/lists.xml", screenName),
                                        false,
                                        new KeyValuePair<string, string>("name", listName),
                                        new KeyValuePair<string, string>("mode", listModeParam),
                                        new KeyValuePair<string, string>("description", description));

            var doc = XDocument.Parse(result);
            return ParseList(doc.Element("list"));
        }

        /// <summary>
        /// Creates a new list for the authenticated user
        /// </summary>
        /// <param name="listName">The name for the list</param>
        /// <param name="user">Twitter user - must be valid, doensn't have to be the authenticated user</param>
        /// <param name="description">The description to give the list</param>
        /// <param name="mode">Whether your list is public or private</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>Created list</returns>
        /// <remarks>Accounts are limited to 20 lists.</remarks>
        public TwitterList CreateList(string listName, TwitterUser user, string description, ListMode mode)
        {
            if (string.IsNullOrEmpty(listName)) throw new ArgumentNullException("listName");
            if (user == null) throw new ArgumentNullException("user");
            return string.IsNullOrEmpty(description) ? CreateList(listName, user.ScreenName, mode) : CreateList(listName, user.ScreenName, description, mode);
        }

        #endregion

        #region EditList

        /// <summary>
        /// Updates the specified list.
        /// </summary>
        /// <param name="listId">Id of the list to update</param>
        /// <param name="screenName">Twitter username - list to be edited MUST belong to the authenticating user</param>
        /// <param name="listName">New name of the list</param>
        /// <param name="mode">Whether your list is public or private</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>Edited list</returns>
        /// <exception cref="ArgumentOutOfRangeException"><c>listId</c> is out of range.</exception>
        public TwitterList EditList(ulong listId, string screenName, string listName, ListMode mode)
        {
            if (string.IsNullOrEmpty(listName)) throw new ArgumentNullException("listName");
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (listId < 1) throw new ArgumentOutOfRangeException("listId");

            string listModeParam;

            switch (mode)
            {
                case ListMode.Private:
                    listModeParam = "private";
                    break;
                default:
                    listModeParam = "public";
                    break;
            }

            var result = SendRequest(RequestMethod.Post,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}.xml", screenName, listId),
                                        false,
                                        new KeyValuePair<string, string>("name", listName),
                                        new KeyValuePair<string, string>("mode", listModeParam));
            var doc = XDocument.Parse(result);
            return ParseList(doc.Element("list"));
        }

        /// <summary>
        /// Updates the specified list.
        /// </summary>
        /// <param name="list">Id of the list to update</param>
        /// <param name="newName">New name of the list</param>
        /// <param name="mode">Whether your list is public or private</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>Edited list</returns>
        public TwitterList EditList(TwitterList list, string newName, ListMode mode)
        {
            if (string.IsNullOrEmpty(newName)) throw new ArgumentNullException("newName");
            if (list == null) throw new ArgumentNullException("list");

            return EditList(list.Id, list.Owner.ScreenName, newName, mode);
        }

        /// <summary>
        /// Updates the specified list.
        /// </summary>
        /// <param name="listId">Id of the list to update</param>
        /// <param name="listName">New name of the list</param>
        /// <param name="screenName">Twitter username - lsit to be edited MUST belong to the authenticating user</param>
        /// <param name="mode">Whether your list is public or private</param>
        /// <param name="description">New description of the list</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>Edited list</returns>
        public TwitterList EditList(ulong listId, string listName, string screenName, ListMode mode, string description)
        {
            if (string.IsNullOrEmpty(listName)) throw new ArgumentNullException("listName");
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (string.IsNullOrEmpty(description)) return EditList(listId, screenName, listName, mode);

            string listModeParam;

            switch (mode)
            {
                case ListMode.Private:
                    listModeParam = "private";
                    break;
                default:
                    listModeParam = "public";
                    break;
            }

            var result = SendRequest(RequestMethod.Post,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}.xml", screenName, listId),
                                        false,
                                        new KeyValuePair<string, string>("name", listName),
                                        new KeyValuePair<string, string>("mode", listModeParam),
                                        new KeyValuePair<string, string>("description", description));

            var doc = XDocument.Parse(result);
            return ParseList(doc.Element("list"));
        }

        /// <summary>
        /// Updates the specified list.
        /// </summary>
        /// <param name="list">The list to update</param>
        /// <param name="listName">New name of the list</param>
        /// <param name="mode">Whether your list is public or private</param>
        /// <param name="description">New description of the list</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>Edited list</returns>
        public TwitterList EditList(TwitterList list, string listName, ListMode mode, string description)
        {
            if (string.IsNullOrEmpty(listName)) throw new ArgumentNullException("listName");
            return string.IsNullOrEmpty(description) ? EditList(list.Id, list.Owner.ScreenName, listName, mode) : EditList(list.Id, listName, list.Owner.ScreenName, mode, description);
        }

        #endregion

        #region GetLists

        /// <summary>
        /// Gets the lists of the specified user.
        /// </summary>
        /// <param name="screenName">Screen name of the user whose lists to retrieve</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns></returns>
        /// <remarks>Private lists will be included if the authenticated users is the same as the user who's lists are being returned.</remarks>
        public Collection<TwitterList> GetLists(string screenName)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists.xml", screenName),
                                        true);

            var doc = XDocument.Parse(result);

            // ReSharper disable PossibleNullReferenceException
            return new Collection<TwitterList>(doc.Element("lists_list").Element("lists").Elements("list").Select(ParseList).ToList());
            // ReSharper restore PossibleNullReferenceException
        }

        /// <summary>
        /// Gets the lists of the specified user.
        /// </summary>
        /// <param name="user">The user whose lists to retrieve</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns></returns>
        /// <remarks>Private lists will be included if the authenticated users is the same as the user who's lists are being returned.</remarks>
        public Collection<TwitterList> GetLists(TwitterUser user)
        {
            if (user == null) throw new ArgumentNullException("user");

            return GetLists(user.ScreenName);
        }

        ///<summary>
        /// Gets the lists of the specified user.
        ///</summary>
        ///<param name="screenName">Screen name of the user whose lists to retrieve</param>
        ///<param name="cursor">Breaks the results into pages. A single page contains 20 lists. Provide a value of -1 to begin paging</param>
        ///<param name="previousCursor">Start index of the previous page</param>
        ///<param name="nextCursor">Start index of the next page</param>
        ///<returns>List of lists</returns>
        /// <remarks>Private lists will be included if the authenticated users is the same as the user who's lists are being returned.</remarks>
        ///<exception cref="ArgumentNullException"></exception>
        public Collection<TwitterList> GetLists(string screenName, long cursor, out long previousCursor, out long nextCursor)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists.xml", screenName),
                                        true,
                                        new KeyValuePair<string, string>("cursor", cursor.ToString()));

            var doc = XDocument.Parse(result);

            var listIdsElement = doc.Element("lists_list");
            // ReSharper disable PossibleNullReferenceException
            var lists = new Collection<TwitterList>(listIdsElement.Element("lists").Elements("list").Select(ParseList).ToList());
            previousCursor = long.Parse(listIdsElement.Element("previous_cursor").Value);
            nextCursor = long.Parse(listIdsElement.Element("next_cursor").Value);
            // ReSharper restore PossibleNullReferenceException
            return lists;
        }

        ///<summary>
        /// Gets the lists of the specified user.
        ///</summary>
        ///<param name="user">The user whose lists to retrieve</param>
        ///<param name="cursor">Breaks the results into pages. A single page contains 20 lists. Provide a value of -1 to begin paging</param>
        ///<param name="previousCursor">Start index of the previous page</param>
        ///<param name="nextCursor">Start index of the next page</param>
        ///<returns>List of lists</returns>
        /// <remarks>Private lists will be included if the authenticated users is the same as the user who's lists are being returned.</remarks>
        ///<exception cref="ArgumentNullException"></exception>
        public Collection<TwitterList> GetLists(TwitterUser user, long cursor, out long previousCursor, out long nextCursor)
        {
            if (user == null) throw new ArgumentNullException("user");

            return GetLists(user.ScreenName, cursor, out previousCursor, out nextCursor);
        }

        #endregion

        #region GetList

        /// <summary>
        /// Retrieves the specified list.
        /// </summary>
        /// <param name="screenName">Screen name of the user whose list to retrieve</param>
        /// <param name="slug">Slug of the list to be retrieved</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>List</returns>
        public TwitterList GetList(string screenName, string slug)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (string.IsNullOrEmpty(slug)) throw new ArgumentNullException("slug");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}.xml", screenName, slug),
                                        true);

            var doc = XDocument.Parse(result);

            return ParseList(doc.Element("list"));
        }

        /// <summary>
        /// Retrieves the specified list.
        /// </summary>
        /// <param name="screenName">Screen name of the user whose list to retrieve</param>
        /// <param name="listId">Id if the list to be retrieved</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <returns>List</returns>
        public TwitterList GetList(string screenName, ulong listId)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (listId < 1) throw new ArgumentOutOfRangeException("listId");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}.xml", screenName, listId),
                                        true);

            var doc = XDocument.Parse(result);

            return ParseList(doc.Element("list"));
        }

        /// <summary>
        /// Retrieves the specified list.
        /// </summary>
        /// <param name="user">The user whose list to retrieve</param>
        /// <param name="listId">Id of the list to be retrieved</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <returns>List</returns>
        public TwitterList GetList(TwitterUser user, ulong listId)
        {
            if (user == null) throw new ArgumentNullException("user");
            if (listId < 1) throw new ArgumentOutOfRangeException("listId");

            return GetList(user.ScreenName, listId);
        }

        /// <summary>
        /// Retrieves the specified list.
        /// </summary>
        /// <param name="user">The user whose list to retrieve</param>
        /// <param name="slug">Slug of the list to be retrieved</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <returns>List</returns>
        public TwitterList GetList(TwitterUser user, string slug)
        {
            if (user == null) throw new ArgumentNullException("user");
            if (string.IsNullOrEmpty(slug)) throw new ArgumentNullException("slug");

            return GetList(user.ScreenName, slug);
        }

        #endregion

        #region DeleteList

        /// <summary>
        /// Deletes the specified list. 
        /// </summary>
        /// <param name="screenName">Any valid Twitter screen name</param>
        /// <param name="slug">Slug of the list which is to be deleted</param>
        /// <remarks>Specified list must be owned by the authenticated user.</remarks>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>List</returns>
        public void DeleteList(string screenName, string slug)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (string.IsNullOrEmpty(slug)) throw new ArgumentNullException("slug");

            SendRequest(RequestMethod.Delete,
                        string.Format("https://api.twitter.com/1/{0}/lists/{1}.xml", screenName, slug),
                        false);
        }

        /// <summary>
        /// Deletes the specified list.
        /// </summary>
        /// <param name="screenName">Any valid Twitter screen name</param>
        /// <param name="listId">Id if the list to delete</param>
        /// <remarks>Must be owned by the authenticated user.</remarks>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <returns>List</returns>
        public void DeleteList(string screenName, ulong listId)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (listId < 1) throw new ArgumentOutOfRangeException("listId");

            SendRequest(RequestMethod.Delete,
                        string.Format("https://api.twitter.com/1/{0}/lists/{1}.xml", screenName, listId),
                        false);
        }

        /// <summary>
        /// Deletes the specified list. 
        /// </summary>
        /// <param name="list">The list to be deleted</param>
        /// <remarks>Specified list must be owned by the authenticated user.</remarks>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>List</returns>
        public void DeleteList(TwitterList list)
        {
            if (list == null) throw new ArgumentNullException("list");

            DeleteList(list.Owner.ScreenName, list.Slug);
        }

        #endregion

        #region GetListStatuses

        /// <summary>
        /// Shows tweet timeline for members of the specified list.
        /// </summary>
        /// <param name="screenName">Screen name of the user whose list to retrieve</param>
        /// <param name="listSlug">Slug of the list which is to be retrieved</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>List of tweets</returns>
        /// <remarks>This method returns at most 20 statuses.</remarks>
        public Collection<Tweet> GetListStatuses(string screenName, string listSlug)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (string.IsNullOrEmpty(listSlug)) throw new ArgumentNullException("listSlug");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}/statuses.xml", screenName,
                                                      listSlug),
                                        true);

            return ParseTimeline(result);
        }

        /// <summary>
        /// Shows tweet timeline for members of the specified list.
        /// </summary>
        /// <param name="screenName">Screen name of the user whose list to retrieve</param>
        /// <param name="listId">Id of the list to be retrieved</param>
        /// <remarks>This method returns at most 20 statuses.</remarks>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <returns>List of tweets</returns>
        public Collection<Tweet> GetListStatuses(string screenName, ulong listId)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (listId < 1) throw new ArgumentOutOfRangeException("listId");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}/statuses.xml", screenName,
                                                      listId),
                                        true);

            return ParseTimeline(result);
        }

        /// <summary>
        /// Gets the list statuses.
        /// </summary>
        /// <param name="screenName">Screen name of the list owner</param>
        /// <param name="listSlug">The slug of the target list</param>
        /// <param name="page">Specifies the page of results to retrieve</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>List of tweets</returns>
        public Collection<Tweet> GetListStatuses(string screenName, string listSlug, uint page)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (string.IsNullOrEmpty(listSlug)) throw new ArgumentNullException("listSlug");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}/statuses.xml", screenName,
                                                      listSlug),
                                        true,
                                        new KeyValuePair<string, string>("page", page.ToString()));

            return ParseTimeline(result);
        }

        ///<summary>
        /// Show tweet timeline for members of the specified list.
        ///</summary>
        ///<param name="screenName">Screen name of the list owner</param>
        ///<param name="listId">Id of the list to be retrieved</param>
        ///<param name="page">Specifies the page of results to retrieve</param>
        ///<exception cref="ArgumentOutOfRangeException"></exception>
        ///<returns>List of tweets</returns>
        ///<exception cref="ArgumentNullException"></exception>
        public Collection<Tweet> GetListStatuses(string screenName, ulong listId, uint page)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (listId < 1) throw new ArgumentOutOfRangeException("listId");
            if (page < 1) throw new ArgumentOutOfRangeException("page");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}/statuses.xml", screenName,
                                                      listId),
                                        true,
                                        new KeyValuePair<string, string>("page", page.ToString()));

            return ParseTimeline(result);
        }

        ///<summary>
        /// Show tweet timeline for members of the specified list.
        ///</summary>
        ///<param name="screenName">Screen name of the list owner</param>
        ///<param name="listSlug">The slug of the target list</param>
        ///<param name="page">Specifies the page of results to retrieve</param>
        ///<param name="perPage">Number of tweets per page</param>
        ///<exception cref="ArgumentNullException"></exception>
        ///<exception cref="ArgumentOutOfRangeException"></exception>
        ///<returns>List of tweets</returns>
        public Collection<Tweet> GetListStatuses(string screenName, string listSlug, uint page, uint perPage)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (string.IsNullOrEmpty(listSlug)) throw new ArgumentNullException("listSlug");
            if (page < 1) throw new ArgumentOutOfRangeException("page");
            if (perPage < 1) throw new ArgumentOutOfRangeException("perPage");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}/statuses.xml", screenName,
                                                      listSlug),
                                        true,
                                        new KeyValuePair<string, string>("page", page.ToString()),
                                        new KeyValuePair<string, string>("per_page", perPage.ToString()));

            return ParseTimeline(result);
        }

        ///<summary>
        /// Show tweet timeline for members of the specified list.
        ///</summary>
        ///<param name="screenName">Screen name of the list owner</param>
        ///<param name="listId">Id of the list to be retrieved</param>
        ///<param name="page">Specifies the page of results to retrieve</param>
        ///<param name="perPage">Number of tweets per page</param>
        ///<exception cref="ArgumentOutOfRangeException"></exception>
        ///<returns>List of tweets</returns>
        ///<exception cref="ArgumentNullException"></exception>
        public Collection<Tweet> GetListStatuses(string screenName, ulong listId, uint page, uint perPage)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (listId < 1) throw new ArgumentOutOfRangeException("listId");
            if (page < 1) throw new ArgumentOutOfRangeException("page");
            if (perPage < 1) throw new ArgumentOutOfRangeException("perPage");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}/statuses.xml", screenName,
                                                      listId),
                                        true,
                                        new KeyValuePair<string, string>("page", page.ToString()),
                                        new KeyValuePair<string, string>("per_page", perPage.ToString()));

            return ParseTimeline(result);
        }

        /// <summary>
        /// Shows tweet timeline for members of the specified list.
        /// </summary>
        /// <param name="screenName">Screen name of the user whose list to retrieve</param>
        /// <param name="listSlug">Slug of the list which is to be retrieved</param>
        /// <param name="sinceId">Returns results with an ID greater than (that is, more recent than) the specified ID</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <returns>List of tweets</returns>
        /// <remarks>This method returns at most 20 statuses.</remarks>
        public Collection<Tweet> GetListStatuses(string screenName, string listSlug, ulong sinceId)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (string.IsNullOrEmpty(listSlug)) throw new ArgumentNullException("listSlug");
            if (sinceId < 1) throw new ArgumentOutOfRangeException("sinceId");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}/statuses.xml", screenName,
                                                      listSlug),
                                        true,
                                        new KeyValuePair<string, string>("since_id", sinceId.ToString()));

            return ParseTimeline(result);
        }

        /// <summary>
        /// Shows tweet timeline for members of the specified list.
        /// </summary>
        /// <param name="screenName">Screen name of the user whose list to retrieve</param>
        /// <param name="listId">Id of the list bo retrieved</param>
        /// <param name="sinceId">Returns results with an ID greater than (that is, more recent than) the specified ID</param>
        /// <remarks>This method returns at most 20 statuses.</remarks>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <returns>List of tweets</returns>
        public Collection<Tweet> GetListStatuses(string screenName, ulong listId, ulong sinceId)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (listId < 1) throw new ArgumentOutOfRangeException("listId");
            if (sinceId < 1) throw new ArgumentOutOfRangeException("sinceId");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}/statuses.xml", screenName,
                                                      listId),
                                        true,
                                        new KeyValuePair<string, string>("since_id", sinceId.ToString()));

            return ParseTimeline(result);
        }

        ///<summary>
        /// Show tweet timeline for members of the specified list.
        ///</summary>
        ///<param name="screenName">Screen name of the user whose list to retrieve</param>
        ///<param name="listSlug">Slug of the list which is to be retrieved</param>
        ///<param name="sinceId">Returns results with an ID greater than (that is, more recent than) the specified ID</param>
        ///<param name="page">Specifies the page of results to retrieve</param>
        ///<exception cref="ArgumentOutOfRangeException"></exception>
        ///<returns></returns>
        ///<exception cref="ArgumentNullException"></exception>
        public Collection<Tweet> GetListStatuses(string screenName, string listSlug, ulong sinceId, uint page)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (string.IsNullOrEmpty(listSlug)) throw new ArgumentNullException("listSlug");
            if (sinceId < 1) throw new ArgumentOutOfRangeException("sinceId");
            if (page < 1) throw new ArgumentOutOfRangeException("page");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}/statuses.xml", screenName,
                                                      listSlug),
                                        true,
                                        new KeyValuePair<string, string>("since_id", sinceId.ToString()),
                                        new KeyValuePair<string, string>("page", page.ToString()));

            return ParseTimeline(result);
        }

        ///<summary>
        /// Show tweet timeline for members of the specified list.
        ///</summary>
        ///<param name="screenName">Screen name of the user whose list to retrieve</param>
        ///<param name="listId">Id of the list bo retrieved</param>
        ///<param name="sinceId">Returns results with an ID greater than (that is, more recent than) the specified ID</param>
        ///<param name="page">Specifies the page of results to retrieve</param>
        ///<exception cref="ArgumentOutOfRangeException"></exception>
        ///<returns>List of tweets</returns>
        ///<exception cref="ArgumentNullException"></exception>
        public Collection<Tweet> GetListStatuses(string screenName, ulong listId, ulong sinceId, uint page)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (listId < 1) throw new ArgumentOutOfRangeException("listId");
            if (sinceId < 1) throw new ArgumentOutOfRangeException("sinceId");
            if (page < 1) throw new ArgumentOutOfRangeException("page");


            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}/statuses.xml", screenName,
                                                      listId),
                                        true,
                                        new KeyValuePair<string, string>("since_id", sinceId.ToString()),
                                        new KeyValuePair<string, string>("page", page.ToString()));

            return ParseTimeline(result);
        }

        ///<summary>
        /// Show tweet timeline for members of the specified list.
        ///</summary>
        ///<param name="screenName">Screen name of the user whose list to retrieve</param>
        ///<param name="listSlug">Slug of the list which is to be retrieved</param>
        ///<param name="sinceId">Returns results with an ID greater than (that is, more recent than) the specified ID</param>
        ///<param name="page">Specifies the page of results to retrieve</param>
        ///<param name="perPage">Specifies the page of results to retrieve</param>
        ///<exception cref="ArgumentOutOfRangeException"></exception>
        ///<returns>List of tweets</returns>
        ///<exception cref="ArgumentNullException"></exception>
        public Collection<Tweet> GetListStatuses(string screenName, string listSlug, ulong sinceId, uint page, uint perPage)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (string.IsNullOrEmpty(listSlug)) throw new ArgumentNullException("listSlug");
            if (sinceId < 1) throw new ArgumentOutOfRangeException("sinceId");
            if (page < 1) throw new ArgumentOutOfRangeException("page");
            if (perPage < 1) throw new ArgumentOutOfRangeException("perPage");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}/statuses.xml", screenName,
                                                      listSlug),
                                        true,
                                        new KeyValuePair<string, string>("since_id", sinceId.ToString()),
                                        new KeyValuePair<string, string>("page", page.ToString()),
                                        new KeyValuePair<string, string>("per_page", perPage.ToString()));

            return ParseTimeline(result);
        }

        ///<summary>
        /// Show tweet timeline for members of the specified list.
        ///</summary>
        ///<param name="screenName">Screen name of the user whose list to retrieve</param>
        ///<param name="listId">Slug of the list which is to be retrieved</param>
        ///<param name="sinceId">Returns results with an ID greater than (that is, more recent than) the specified ID</param>
        ///<param name="page">Specifies the page of results to retrieve</param>
        ///<param name="perPage">Specifies the page of results to retrieve</param>
        ///<exception cref="ArgumentOutOfRangeException"></exception>
        ///<returns>List of tweets</returns>
        ///<exception cref="ArgumentNullException"></exception>
        public Collection<Tweet> GetListStatuses(string screenName, ulong listId, ulong sinceId, uint page, uint perPage)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (listId < 1) throw new ArgumentOutOfRangeException("listId");
            if (sinceId < 1) throw new ArgumentOutOfRangeException("sinceId");
            if (page < 1) throw new ArgumentOutOfRangeException("page");
            if (perPage < 1) throw new ArgumentOutOfRangeException("perPage");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/{1}/statuses.xml", screenName,
                                                      listId),
                                        true,
                                        new KeyValuePair<string, string>("since_id", sinceId.ToString()),
                                        new KeyValuePair<string, string>("page", page.ToString()),
                                        new KeyValuePair<string, string>("per_page", perPage.ToString()));

            return ParseTimeline(result);
        }

        #endregion

        #region GetListMemberships

        /// <summary>
        /// Gets the lists the specified user has been added to.
        /// </summary>
        /// <param name="screenName">Screen name of the target user</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>List of TwitterLists</returns>
        public Collection<TwitterList> GetListMemberships(string screenName)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/memberships.xml", screenName),
                                        true);

            var doc = XDocument.Parse(result);

            // ReSharper disable PossibleNullReferenceException
            return new Collection<TwitterList>(doc.Element("lists_list").Element("lists").Elements("list").Select(ParseList).ToList());
            // ReSharper restore PossibleNullReferenceException
        }

        /// <summary>
        /// Gets the lists the specified user has been added to.
        /// </summary>
        /// <param name="screenName">Screen name of the target user</param>
        /// <param name="cursor">Breaks the results into pages. Provide a value of -1 to begin paging.</param>
        /// <param name="previousCursor">Start index of the previous page</param>
        /// <param name="nextCursor">Start index of the next page</param>
        /// <remarks>One page consists of 20 items.</remarks>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>List of TwitterLists</returns>
        public Collection<TwitterList> GetListMemberships(string screenName, long cursor, out long previousCursor,
                                                    out long nextCursor)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (cursor < -1) throw new ArgumentOutOfRangeException("cursor");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/memberships.xml", screenName),
                                        true,
                                        new KeyValuePair<string, string>("cursor", cursor.ToString()));

            var doc = XDocument.Parse(result);
            var listsListElement = doc.Element("lists_list");

            var lists = new Collection<TwitterList>();
            previousCursor = 0;
            nextCursor = 0;
            if (listsListElement != null)
            {
                // ReSharper disable PossibleNullReferenceException
                lists = new Collection<TwitterList>(listsListElement.Element("lists").Elements("list").Select(ParseList).ToList());

                previousCursor = long.Parse(listsListElement.Element("previous_cursor").Value);
                nextCursor = long.Parse(listsListElement.Element("next_cursor").Value);
                // ReSharper restore PossibleNullReferenceException
            }
            return lists;
        }

        /// <summary>
        /// Gets the lists the specified user has been added to.
        /// </summary>
        /// <param name="user">The target user</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>List of TwitterLists</returns>
        public Collection<TwitterList> GetListMemberships(TwitterUser user)
        {
            if (user == null) throw new ArgumentNullException("user");

            return GetListMemberships(user.ScreenName);
        }

        /// <summary>
        /// Gets the lists the specified user has been added to.
        /// </summary>
        /// <param name="user">The target user</param>
        /// <param name="cursor">Breaks the results into pages. Provide a value of -1 to begin paging.</param>
        /// <param name="previousCursor">Start index of the previous page</param>
        /// <param name="nextCursor">Start index of the next page</param>
        /// <remarks>One page consists of 20 items.</remarks>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>List of TwitterLists</returns>
        public Collection<TwitterList> GetListMemberships(TwitterUser user, long cursor, out long previousCursor,
                                                    out long nextCursor)
        {
            if (user == null) throw new ArgumentNullException("user");
            if (cursor < -1) throw new ArgumentOutOfRangeException("cursor");

            return GetListMemberships(user.ScreenName, cursor, out previousCursor, out nextCursor);
        }

        #endregion

        #region GetFollowedLists

        /// <summary>
        /// Gets the lists the specified user follows.
        /// </summary>
        /// <param name="screenName">Screen name of the target user</param>
        /// <returns>List of TwitterLists</returns>
        /// <exception cref="ArgumentNullException">Argument is null.</exception>
        public Collection<TwitterList> GetFollowedLists(string screenName)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/subscriptions.xml",
                                                      screenName),
                                        true);

            var doc = XDocument.Parse(result);

            // ReSharper disable PossibleNullReferenceException
            return new Collection<TwitterList>(doc.Element("lists_list").Element("lists").Elements("list").Select(ParseList).ToList());
            // ReSharper restore PossibleNullReferenceException
        }

        /// <summary>
        /// Gets the lists the specified user follows.
        /// </summary>
        /// <param name="screenName">Screen name of the target user</param>
        /// <param name="cursor">Breaks the results into pages. Provide a value of -1 to begin paging.</param>
        /// <param name="previousCursor">Start index of the previous page</param>
        /// <param name="nextCursor">Start index of the next page</param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>List of TwitterLists</returns>
        public Collection<TwitterList> GetFollowedLists(string screenName, long cursor, out long previousCursor,
                                                  out long nextCursor)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            if (cursor < -1) throw new ArgumentOutOfRangeException("cursor");

            var result = SendRequest(RequestMethod.Get,
                                        string.Format("https://api.twitter.com/1/{0}/lists/subscriptions.xml",
                                                      screenName),
                                        true,
                                        new KeyValuePair<string, string>("cursor", cursor.ToString()));

            var doc = XDocument.Parse(result);
            var listsListElement = doc.Element("lists_list");

            var lists = new Collection<TwitterList>();
            previousCursor = 0;
            nextCursor = 0;
            if (listsListElement != null)
            {
                // ReSharper disable PossibleNullReferenceException
                lists = new Collection<TwitterList>(listsListElement.Element("lists").Elements("list").Select(ParseList).ToList());

                previousCursor = long.Parse(listsListElement.Element("previous_cursor").Value);
                nextCursor = long.Parse(listsListElement.Element("next_cursor").Value);
                // ReSharper restore PossibleNullReferenceException
            }
            return lists;
        }

        /// <summary>
        /// Gets the lists the specified user follows.
        /// </summary>
        /// <param name="user">SThe target user</param>
        /// <returns>List of TwitterLists</returns>
        /// <exception cref="ArgumentNullException">Argument is null.</exception>
        public Collection<TwitterList> GetFollowedLists(TwitterUser user)
        {
            if (user == null) throw new ArgumentNullException("user");

            return GetFollowedLists(user.ScreenName);
        }

        /// <summary>
        /// Gets the lists the specified user follows.
        /// </summary>
        /// <param name="user">The target user</param>
        /// <param name="cursor">Breaks the results into pages. Provide a value of -1 to begin paging.</param>
        /// <param name="previousCursor">Start index of the previous page</param>
        /// <param name="nextCursor">Start index of the next page</param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>List of TwitterLists</returns>
        public Collection<TwitterList> GetFollowedLists(TwitterUser user, long cursor, out long previousCursor,
                                                  out long nextCursor)
        {
            if (user == null) throw new ArgumentNullException("user");
            if (cursor < -1) throw new ArgumentOutOfRangeException("cursor");

            return GetFollowedLists(user.ScreenName, cursor, out previousCursor, out nextCursor);
        }

        #endregion

        #region GetAllLists

        /// <summary>
        /// Returns all lists the authenticating user subscribes to, including their own.
        /// </summary>
        /// <returns></returns>
        public Collection<TwitterList> GetAllLists()
        {
            var result = SendRequest(RequestMethod.Get, "https://api.twitter.com/1/lists/all.xml", true);

            var doc = XDocument.Parse(result);

            var root = doc.Element("lists");
            var resultCol = new Collection<TwitterList>();

            // ReSharper disable PossibleNullReferenceException
            foreach (var listElement in root.Elements("list"))
            {
                resultCol.Add(ParseList(listElement));
            }
            // ReSharper restore PossibleNullReferenceException

            return resultCol;
        }

        /// <summary>
        /// Returns all lists the specified user subscribes to, including their own.
        /// </summary>
        /// <param name="screenName">Screen name of the target user</param>
        /// <returns>Collection of lists</returns>
        /// <exception cref="ArgumentNullException">Argument is null.</exception>
        public Collection<TwitterList> GetAllLists(string screenName)
        {
            if (string.IsNullOrEmpty(screenName)) throw new ArgumentNullException("screenName");
            var result = SendRequest(RequestMethod.Get, "https://api.twitter.com/1/lists/all.xml", true, new KeyValuePair<string, string>("screen_name", screenName));

            var doc = XDocument.Parse(result);

            var root = doc.Element("lists");
            var resultCol = new Collection<TwitterList>();

            // ReSharper disable PossibleNullReferenceException
            foreach (var listElement in root.Elements("list"))
            {
                resultCol.Add(ParseList(listElement));
            }
            // ReSharper restore PossibleNullReferenceException

            return resultCol;

        }

        /// <summary>
        /// Returns all lists the specified user subscribes to, including their own.
        /// </summary>
        /// <param name="userId">ID of the target user</param>
        /// <returns>Collection of lists</returns>
        /// <exception cref="ArgumentNullException">Argument is null.</exception>
        public Collection<TwitterList> GetAllLists(ulong userId)
        {
            if (userId < 1) throw new ArgumentNullException("userId");
            var result = SendRequest(RequestMethod.Get, "https://api.twitter.com/1/lists/all.xml", true, new KeyValuePair<string, string>("user_id", userId.ToString()));

            var doc = XDocument.Parse(result);

            var root = doc.Element("lists");
            var resultCol = new Collection<TwitterList>();

            // ReSharper disable PossibleNullReferenceException
            foreach (var listElement in root.Elements("list"))
            {
                resultCol.Add(ParseList(listElement));
            }
            // ReSharper restore PossibleNullReferenceException

            return resultCol;
        }

        /// <summary>
        /// Returns all lists the specified user subscribes to, including their own.
        /// </summary>
        /// <param name="user">The target user</param>
        /// <returns>Collection of lists</returns>
        /// <exception cref="ArgumentNullException">Argument is null.</exception>
        public Collection<TwitterList> GetAllLists(TwitterUser user)
        {
            if (user == null) throw new ArgumentNullException("user");
            return GetAllLists(user.ScreenName);
        }

        #endregion

        #endregion

        /// <summary>
        /// Parses Twitter list from XML data
        /// </summary>
        /// <param name="listElement">XML node containing list data</param>
        /// <returns>Twitter list</returns>
        private static TwitterList ParseList(XElement listElement)
        {
            TwitterList twitterList = null;
            if (listElement != null)
                // ReSharper disable PossibleNullReferenceException
                twitterList = new TwitterList(ulong.Parse(listElement.Element("id").Value),
                                              listElement.Element("name").Value,
                                              listElement.Element("full_name").Value,
                                              listElement.Element("slug").Value,
                                              listElement.Element("description").Value,
                                              ulong.Parse(listElement.Element("subscriber_count").Value),
                                              ulong.Parse(listElement.Element("member_count").Value),
                                              listElement.Element("uri").Value,
                                              listElement.Element("mode").Value,
                                              ParseAuthor(listElement.Element("user")));
            // ReSharper restore PossibleNullReferenceException
            return twitterList;
        }
    }
}
